/*
 *  Author: Gary Kelly, gkelly2008@my.fit.edu
 *  Author: John Stevens, jstevens@fit.edu
 *  Class:  CSE 4232 - Network Programming
 *  Program: SQlite database synchronization client program
 */

#include <stdio.h>
#include <sqlite3.h>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <libtasn1.h>
#include <iostream>
#include <errno.h>

using namespace std;

#define BUF_SIZE 512

//#include "database_asn1_tab.c"
const ASN1_ARRAY_TYPE database_asn1_tab[]={
  {"DatabaseMessagesModule",536875024,0},
  {0,1610612748,0},
  {"iso",1073741825,"1"},
  {"identified-organization",1073741825,"3"},
  {"dod",1073741825,"6"},
  {"internet",1073741825,"1"},
  {"security",1073741825,"5"},
  {"mechanisms",1073741825,"5"},
  {"sqliteserver",1,"108"},
  {"SyncRequest",1610612741},
  {"lastSnapshot",1082130449,0},
  {"tableNames",536870923,0},
  {0,2,"TableName"},
  {"Database",1610612741,0},
  {"tables",1610612747,0},
  {0,2,"Table"},
  {"name",1073741826,"DatabaseName"},
  {"snapshot",8388625,0},
  {"Table",1610612741,0},
  {"name",1073741826,"TableName"},
  {"fields",1610612747,0},
  {0,2,"FieldName"},
  {"fieldTypes",1610612747,0},
  {0,2,"FieldType"},
  {"rows",536870923,0},
  {0,2,"Row"},
  {"Row",1610612747,0},
  {0,2,"FieldValue"},
  {"FieldValue",1610612754,0},
  {"value",1073741831,0},
  {"empty",20,0},
  {"TableName",1610620930,"UTF8String"},
  {0,4104,"0"},
  {"DatabaseName",1610620930,"UTF8String"},
  {0,4104,"1"},
  {"FieldName",1610620930,"UTF8String"},
  {0,4104,"2"},
  {"FieldType",1610620930,"PrintableString"},
  {0,4104,"3"},
  {"UTF8String",1610620935,0},
  {0,4360,"12"},
  {"PrintableString",536879111,0},
  {0,4360,"19"},
  {0,0,0}
};


char * database = "/udrive/staff/jstevens/milestone4/deliberation2.db";

int clientSocket;	
struct sockaddr_in serverAddr;

void connectToServer(char * server);
void updateDatabase(int clientSocket);

static int callback(void *NotUsed, int argc, char **argv, char **azColName){
    int i;
    for(i=0; i<argc; i++){
      //printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
      
      if(argv[i])
      	connectToServer(argv[i]);
      
      
    }
    printf("\n");
    return 0;
}

//Parses and Connects to specified server 
void connectToServer(char * server)
{
	char * token;
	token = strtok (server,":");
	string tcpinfo[2];
	int index = 0;
	
	//Parses String and put
	//entries in an array
	while (token != NULL)
	{
		tcpinfo[index] = token;
		token = strtok (NULL, ":");
		index++;
	}
	
	//create clientSocket 
	if ( (clientSocket = socket (AF_INET, SOCK_STREAM, 0)) == -1) {
		cerr << "socket error: " << strerror (errno);
	}
	
	//clear the socket (required to use it)
	memset (&serverAddr, 0, sizeof (serverAddr));
	//fill in the socket structure with the host's information
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.s_addr = inet_addr(tcpinfo[0].c_str());
	serverAddr.sin_port = htons(atoi(tcpinfo[1].c_str()));
	
	//connect to the host
	if (connect (clientSocket, (struct sockaddr*) &serverAddr, sizeof (serverAddr)) == -1) {
		cerr << "connect error: " << strerror (errno) << endl;
	}
	
	updateDatabase(clientSocket);
	
	for (int index2 = 0; index2 < index2 < 2; index2++)
		printf("%s\n",tcpinfo[index2].c_str());
}

void updateDatabase(int clientSocket){

	char request[BUF_SIZE];
	char response[BUF_SIZE];
	int messageLength = BUF_SIZE;
	int recvSize = BUF_SIZE;
	int err = 0;

	//asn definitions
	ASN1_TYPE def = ASN1_TYPE_EMPTY;
	ASN1_TYPE req = ASN1_TYPE_EMPTY;
	ASN1_TYPE resp = ASN1_TYPE_EMPTY;
	char errorDesc[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
	

	//create the ASN1 structure
	if ( (err = asn1_array2tree (database_asn1_tab, &def, errorDesc)) != ASN1_SUCCESS) {
		cerr << "array2tree error: " << asn1_strerror (err);
	}

	//create a new request
	if ( (err = asn1_create_element (def, "DatabaseMessagesModule.SyncRequest", &req)) != ASN1_SUCCESS) {
		cerr << "create_element error: " << asn1_strerror (err);
	}

	//fill the time
	if ( (err = asn1_write_value (req, "lastSnapshot", "201103151830.02", 0)) != ASN1_SUCCESS) {
		cerr << "write_value lastSnapshot error: " << asn1_strerror (err);
	}

	//fill the table with names
	if ( (err = asn1_write_value (req, "tableNames", "NEW", 0)) != ASN1_SUCCESS) {
		cerr << "write_value tableNames New error: " << asn1_strerror (err);
	}
	
	if ( (err = asn1_write_value (req, "tableNames.?1", "peers", 0)) != ASN1_SUCCESS) {
        cerr << "write_value tableNames.?1 error: " << asn1_strerror (err);
    }
    
    //fill the table name with the user input
	if ( (err = asn1_write_value (req, "tableNames", "NEW", 0)) != ASN1_SUCCESS) {
		cerr << "write_value tableNames New error: " << asn1_strerror (err);
	}
	
	if ( (err = asn1_write_value (req, "tableNames.?2", "peers_addresses", 0)) != ASN1_SUCCESS) {
        cerr << "write_value tableNames.?1 error: " << asn1_strerror (err);
    }
    
    //encoding
	if ( (err = asn1_der_coding (req, "", request, &messageLength, errorDesc)) != ASN1_SUCCESS) {
        cerr << "der_coding error: " << asn1_strerror (err);
    }
	
	//send data
    if (send (clientSocket, request, messageLength, 0) == -1) {
        cerr << "send error: " << errno;
    }
    
    //wait for response
    if ( (recvSize = (recv (clientSocket, response, BUF_SIZE, 0))) == -1) {
				cerr << "recv error: " << errno;	
	}
	
	//create a new request
	if ( (err = asn1_create_element (def, "DatabaseMessagesModule.Database", &resp)) != ASN1_SUCCESS) {
		cerr << "create_element error: " << asn1_strerror (err);
	}
	
	//decoding
	if ( (err = asn1_der_decoding (&resp, response, recvSize, errorDesc)) != ASN1_SUCCESS) {
        cerr << "der_decoding error: " << asn1_strerror (err);
    }
    
    //get tablename
    messageLength = BUF_SIZE;
    if ( (err = asn1_read_value (resp, "tables.?1.name", response, &messageLength)) != ASN1_SUCCESS) {
				cerr << "read_value error: " << asn1_strerror (err);
    }
    
    string tableone (response, messageLength);
    
    //get tablename
    messageLength = BUF_SIZE;
    if ( (err = asn1_read_value (resp, "tables.?2.name", response, &messageLength)) != ASN1_SUCCESS) {
				cerr << "read_value error: " << asn1_strerror (err);
    }
    
    string tabletwo (response, messageLength);
    
    cout << tableone << endl;
    cout << tabletwo << endl;
    
    //Fill array with global id from local database
    
    
    //find the number of columns
	//int fieldCount = 0;
	//if ( (err = asn1_number_of_elements (resp, "fields", &fieldCount)) != ASN1_SUCCESS) {
	//	cerr << "number_of_elements error: " << asn1_strerror (err);
	//	break;
//	}
    
    //fill array with global id using select count(*) from peers; as the count
    //compare global id's in asn struct with current database
    //if current database does not have global id then add it otherwise ignore.
	
}

int main(int argc, char **argv){
    sqlite3 *db;
    char *zErrMsg = 0;
    int rc;
  
    /*if( argc!=3 ){
      fprintf(stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]);
      exit(1);
    }*/
    rc = sqlite3_open(database, &db);
    if( rc ){
      fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
      sqlite3_close(db);
      exit(1);
    }
    rc = sqlite3_exec(db, "Select address from peers_addresses", callback, 0, &zErrMsg);
    if( rc!=SQLITE_OK ){
      fprintf(stderr, "SQL error: %s\n", zErrMsg);
      sqlite3_free(zErrMsg);
    }
    sqlite3_close(db);
    return 0;
} 